package zju.ccnt.rest.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import zju.ccnt.rest.api.ApiCreateBusinessResource;
import zju.ccnt.rest.api.ApiCreateRankList;
import zju.ccnt.rest.api.ApiCreateResourceClass;
import zju.ccnt.rest.api.ApiReplyCreateBusinessClass;
import zju.ccnt.rest.domain.*;
import zju.ccnt.rest.repository.*;
import zju.ccnt.rest.service.exception.InvalidApiInputException;
import zju.ccnt.rest.service.exception.NewItemConflictException;
import zju.ccnt.rest.service.exception.NoPermissionException;
import zju.ccnt.rest.service.util.ServiceUtil;
import zju.ccnt.rest.service.util.constant.RankListType;
import zju.ccnt.rest.service.util.constant.ResourceActionType;
import zju.ccnt.rest.service.util.sql.ViewUtil;

import java.util.*;

/**
 * Created by zha on 14/11/11.
 */
@Service
public class BusinessResourceServiceImpl extends BaseService{
    private Logger LOG = LoggerFactory.getLogger(BusinessUserServiceImpl.class);

    private BusinessResourceRepository businessResourceRepository;
    private BusinessUserRepository businessUserRepository;
    private ResourceRepository resourceRepository;
    private EndUserRepository endUserRepository;
    private ResourceClassRepository resourceClassRepository;
    private RankListRepository rankListRepository;
    private ResourceActionRepository resourceActionRepository;
    private ViewUtil viewUtil;

    @Autowired
    public BusinessResourceServiceImpl(@Qualifier("jdbcSqlUtil")ViewUtil viewUtil,
                                       BusinessResourceRepository businessResourceRepository,
                                       BusinessUserRepository businessUserRepository,
                                       ResourceRepository resourceRepository,
                                       EndUserRepository endUserRepository,
                                       ResourceClassRepository resourceClassRepository,
                                       RankListRepository rankListRepository,
                                       ResourceActionRepository resourceActionRepository
                                       ) {
        super();
        this.businessResourceRepository = businessResourceRepository;
        this.businessUserRepository = businessUserRepository;
        this.resourceRepository = resourceRepository;
        this.endUserRepository = endUserRepository;
        this.resourceClassRepository = resourceClassRepository;
        this.rankListRepository = rankListRepository;
        this.viewUtil = viewUtil;
        this.resourceActionRepository = resourceActionRepository;
    }

    public Page<Map<String, Object>> getResourceRankListContent(String rankId, Authentication authentication, int page, int count){
        RankList rankList = rankListRepository.findOne(rankId);
        if (rankList == null){
            throw new InvalidApiInputException("rankId", rankId);
        }
        //检测授权的用户权限，非public的榜单不允许USER以及非本商家查看内容
        boolean isBusiness = false;
        if (!rankList.isPublic()){
            Collection<GrantedAuthority> auths = (Collection<GrantedAuthority>)authentication.getAuthorities();
            for (GrantedAuthority authority: auths){
                isBusiness = zju.ccnt.oauth2.Role.valueOf(authority.getAuthority()).equals(zju.ccnt.oauth2.Role.BUSINESS);
                if (isBusiness) break;
            }
            if (!isBusiness || !businessUserRepository.findByEmail(authentication.getName()).getId().equals(rankList.getBusinessId()))
                throw new NoPermissionException(authentication.getName(), "getResourceRankListContent", "rankList:" +rankId);
        }

        //todo  sql limit 这写得太烂了，或者就直接查完后继续过滤一遍，貌似不大好
        //todo 榜单创建有几个是默认存在的返回项目，如ID，资源榜单还有HAS_UPLOADED
        String limit = " has_uploaded = 1";
        List<Map<String, Object>> list = viewUtil.QueryAll(rankList.getViewName(), page*count, count, limit);
        long total = viewUtil.count(rankList.getViewName(), limit);

        return new PageImpl<Map<String, Object>>(list, new PageRequest(page, count), total);
    }

    public RankList getResourceRankList(String rankId, String currentUser){
        RankList rankList = rankListRepository.findOne(rankId);
        if (rankList != null){
            if (!businessUserRepository.findByEmail(currentUser).getId().equals(rankList.getBusinessId())){
                LOG.info("User " + currentUser + " has no permission to check resource rank list of business with id:" + rankList.getBusinessId());
                throw new NoPermissionException(currentUser, rankList.getBusinessId(), "getResourceRankList");
            }
        }
        return rankList;
    }

    @Transactional
    public RankList addResourceRankList(ApiCreateRankList apiCreateRankList, String currentUser){
        validate(apiCreateRankList);
        String businessId = apiCreateRankList.getBusinessId();
        BusinessUser current = businessUserRepository.findByEmail(currentUser);
        if (!current.getId().equals(businessId)){
            LOG.info("User " + currentUser + " has no permission to add resource rank list of business with id:" + businessId);
            throw new NoPermissionException(currentUser, businessId, "addResourceClass");
        }

        if (rankListRepository.findByBusinessIdAndNameAndType(apiCreateRankList.getBusinessId(),
                apiCreateRankList.getName(), RankListType.valueOf(apiCreateRankList.getType())) != null){
            throw new NewItemConflictException("RankList", new String[]{"businessId", "name", "type"},
                                new String[]{apiCreateRankList.getBusinessId(), apiCreateRankList.getName(), apiCreateRankList.getType()});
        }

        RankList rankList = new RankList(apiCreateRankList, currentUser);
        rankList.setBusinessUser(current);
        //todo 创建榜单视图
        viewUtil.execute(apiCreateRankList.getSql(), rankList.getViewName());

        return rankListRepository.save(rankList);
    }

    public ResourceClass updateResourceClass(String newName, String classId, String currentUser){
        ResourceClass target = resourceClassRepository.findOne(classId);
        if (!businessUserRepository.findByEmail(currentUser).getId().equals(target.getBusinessId())){
            LOG.info("User " + currentUser + " has no permission to edit resource classes of business with id:" + target.getBusinessId());
            throw new NoPermissionException(currentUser, target.getBusinessId(), "addResourceClass");
        }
        //TODO 验证新名字是否已经存在
        target.setName(newName);
        return resourceClassRepository.save(target);
    }

    public ApiReplyCreateBusinessClass addResourceClass(ApiCreateResourceClass apiCreateResourceClass, String currentUser){
        String businessId = apiCreateResourceClass.getBusinessId();
        //查看当前登陆身份是否为目标查询的商家用户，不符合则不允许进行操作
        //TODO 加个内部的AOP？
        if (!businessUserRepository.findByEmail(currentUser).getId().equals(businessId)){
            LOG.info("User " + currentUser + " has no permission to add resource classes of business with id:" + businessId);
            throw new NoPermissionException(currentUser, businessId, "addResourceClass");
        }

        List<String> classes = apiCreateResourceClass.getClassList();
        if (classes == null || classes.size() == 0){
            throw new InvalidApiInputException("classList", "null");
        }

        List<ResourceClass> toAdd = new ArrayList<ResourceClass>();
        List<ResourceClass> existed = new ArrayList<ResourceClass>();
        for (String classString : classes){
            ResourceClass resourceClass = resourceClassRepository.findByBusinessIdAndName(businessId, classString);
            if ( resourceClass == null){
                toAdd.add(new ResourceClass(classString, businessId));
            } else {
                existed.add(resourceClass);
            }
        }

        Iterable<ResourceClass> iterable = resourceClassRepository.save(toAdd);
        List<ResourceClass> added = new ArrayList<ResourceClass>();
        for (ResourceClass resourceClass: iterable){
            added.add(resourceClass);
            toAdd.remove(resourceClass);
        }


        return new ApiReplyCreateBusinessClass(added, existed, toAdd);
    }

    public ResourceClass getSingleResourceClass(String businessId, String name){
        return resourceClassRepository.findByBusinessIdAndName(businessId, name);
    }

    public BusinessResource getSingleBusinessResource(String id){
        return businessResourceRepository.findOne(id);
    }

    public BusinessResource removeBusinessResource(String id, String email){
        BusinessResource businessResource = getSingleBusinessResource(id);
        if (businessResource == null) {
            throw new InvalidApiInputException("id", id);
        }
        if (!businessResource.getBusinessUser().getEmail().equals(email)){
            //当前EMAIL对应商家（即授权登陆用户）非该资源所属商家，不允许进行删除操作
            throw new NoPermissionException(email, id, "delete");
        }
        businessResourceRepository.delete(id);
        return businessResource;
    }


    /**
     * 按照资源类型获取商家资源
     * @param currentUser
     * @param businessId
     * @param resourceClassId
     * @param page
     * @param size
     * @param sort
     * @return
     */
    public Page<BusinessResource> getBusinessResource(String currentUser, String businessId, String resourceClassId, int page, int size, String[] sort){
        //查看当前登陆身份是否为目标查询的商家用户，不符合则不允许进行查询操作
        if (!businessUserRepository.findByEmail(currentUser).getId().equals(businessId)){
            LOG.info("User " + currentUser + " has no permission to read resources of business with id:" + businessId);
            throw new NoPermissionException(currentUser, businessId, "getBusinessResource");
        }

       return businessResourceRepository.findByBusinessIdAndClassId(
               businessId, resourceClassId,
                new PageRequest(page, size, ServiceUtil.getSorter(sort)));
    }

    public BusinessResource getBusinessResource(String businessId, String hashValue){
        return businessResourceRepository.findByBusinessIdAndHashValue(businessId, hashValue);
    }

    /**
     * 物理资源表中添加物理资源
     * @param resource
     * @return
     */
     public Resource addResource(Resource resource){
         if (resourceRepository.findByHashValue(resource.getHashValue()) != null){
             throw new NewItemConflictException("Resource", new String[]{"hashValue"}, new String[]{resource.getHashValue()});
         }
         return resourceRepository.save(resource);
     }

    public Resource updateResource(Resource resource){
       return resourceRepository.save(resource);
    }


    public Resource getResource(String hashValue){
        return resourceRepository.findByHashValue(hashValue);
    }

    /**
     * 向商家资源表中添加新的资源信息
     * @param apiBusinessResource
     * @return
     *    BusinessResource, 添加的信息
     * @throws
     */
    public BusinessResource createBusinessResource(ApiCreateBusinessResource apiBusinessResource){
        Resource resource = getResource(apiBusinessResource.getHashValue());
        if (resource == null){
            LOG.error("Resource does not exist for hashValue[{}]", apiBusinessResource.getHashValue());
            throw new InvalidApiInputException("hashValue", apiBusinessResource.getHashValue());
        }
        BusinessResource businessResource = getBusinessResource(apiBusinessResource.getBusinessId(), apiBusinessResource.getHashValue());
        if (businessResource != null) return businessResource;
        //检测上传信息
        BusinessUser businessUser = businessUserRepository.findById(apiBusinessResource.getBusinessId());
        if (businessUser == null){
            throw new InvalidApiInputException("businessId", apiBusinessResource.getBusinessId());
        }
        EndUser endUser = endUserRepository.findById(apiBusinessResource.getUploaderId());
        if (endUser == null){
            throw new InvalidApiInputException("uploaderId", apiBusinessResource.getUploaderId());
        }

        businessResource =businessResourceRepository.save(new BusinessResource(apiBusinessResource));
        //TODO 新插入的元素在EAGER下无法进行自动填充，LAZY未尝试
        businessResource.setBusinessUser(businessUser);
        businessResource.setResource(resource);

        LOG.info("create businessResource hashValue " + apiBusinessResource.getHashValue() + " for business:" + apiBusinessResource.getBusinessId());
        return businessResource;
    }

    public ResourceAction addResourceAction(String hashValue, String businessId, String user, ResourceActionType action){
        BusinessResource resource = businessResourceRepository.findByBusinessIdAndHashValue(businessId, hashValue);
        if (resource == null){
            LOG.error("No buiness_resource found.");
            throw new InvalidApiInputException("businessResource", businessId + ":" +hashValue);
        }
        ResourceAction newAction = new ResourceAction(resource.getId(), action, user);
        return resourceActionRepository.save(newAction);
    }

    public void addResourceAction(String hashValue, String businessId, Iterable<String> users, ResourceActionType action ){
        Set<ResourceAction> actions = new HashSet<ResourceAction>();
        BusinessResource resource = businessResourceRepository.findByBusinessIdAndHashValue(businessId, hashValue);
        if (resource == null){
            LOG.error("No buiness_resource found.");
            throw new InvalidApiInputException("businessResource", businessId + ":" +hashValue);
        }
        for (String user: users){
            actions.add(new ResourceAction(resource.getId(), action, user));
        }
        resourceActionRepository.save(actions);
    }

    @Transactional
    public int uploadedResource(String hashValue){
        Resource resource = getResource(hashValue);
        if (resource == null){
            LOG.error("No resource found for HashValue[{}]", hashValue);
            throw new InvalidApiInputException("hashValue", hashValue);
        }
        resource.setHasUploaded(true);
        //物理资源标记为已上传
        resourceRepository.save(resource);
        //相应的商家资源标记为已上传
        int res = businessResourceRepository.uploadedBusineddResource(hashValue);
        return res;
    }

}
